package com.millstein.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.millstein.context.UserAuthorization;
import com.millstein.enums.BizCodeEnum;
import com.millstein.enums.coupon.CouponStateEnum;
import com.millstein.enums.coupon.LockStateEnum;
import com.millstein.exception.BizException;
import com.millstein.mapper.CouponRecordMapper;
import com.millstein.mapper.CouponTaskMapper;
import com.millstein.model.CouponRecordDO;
import com.millstein.model.CouponRecordMessage;
import com.millstein.model.CouponTaskDO;
import com.millstein.model.LoginUser;
import com.millstein.properties.RabbitMQProperties;
import com.millstein.request.LockCouponRequest;
import com.millstein.service.CouponRecordService;
import com.millstein.vo.CouponRecordVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author millstein
 * @since 2021-09-14
 */
@Service
@Slf4j
@EnableConfigurationProperties(RabbitMQProperties.class)
public class CouponRecordServiceImpl extends ServiceImpl<CouponRecordMapper, CouponRecordDO> implements CouponRecordService {

    @Autowired
    private CouponRecordMapper couponRecordMapper;

    @Autowired
    private CouponTaskMapper couponTaskMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RabbitMQProperties rabbitMQProperties;

    /**
     * 分页条件查询领券记录
     *
     * @param page 当前页
     * @param size 每页记录数
     * @return
     */
    @Override
    public Page<CouponRecordVO> pageCouponRecord(int page, int size) {
        LoginUser loginUser = UserAuthorization.getUserContext();
        QueryWrapper<CouponRecordDO> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", loginUser.getId())
                .orderByDesc("create_time");

        Page<CouponRecordDO> doPage = new Page<>(page, size);
        this.page(doPage, wrapper);
        List<CouponRecordVO> voLIst = doPage.getRecords().stream().map(CouponRecordVO::new).collect(Collectors.toList());

        Page<CouponRecordVO> voPage = new Page<>();
        BeanUtils.copyProperties(doPage, voPage);
        voPage.setRecords(voLIst);

        return voPage;
    }

    /**
     * 根据id查询领券记录
     *
     * @param id 领券记录id
     * @return
     */
    @Override
    public CouponRecordVO findCouponRecord(Long id) {
        LoginUser loginUser = UserAuthorization.getUserContext();
        QueryWrapper<CouponRecordDO> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", loginUser.getId())
                .eq("id", id);

        CouponRecordDO couponRecordDO = this.getOne(wrapper);
        return couponRecordDO == null ? null : new CouponRecordVO(couponRecordDO);
    }


    /**
     * 锁定优惠券
     *
     * @param lockCouponRequest 锁定优惠券需要用到的信息
     */
    @Override
    @Transactional
    public void lockCoupon(LockCouponRequest lockCouponRequest) {
        // 1.获取当前登录信息
        LoginUser loginUser = UserAuthorization.getUserContext();

        // 2.修改优惠券记录的状态
        updateLockStatusForCouponRecord(loginUser.getId(), lockCouponRequest);

        // 3.添加优惠券的锁定记录
        List<CouponTaskDO> list = saveLockTask(lockCouponRequest);

        // 4.发送延时消息，这个消息是当订单没有成功支付时，用来解锁优惠券的
        sendDelayMessageForReleaseLock(list, lockCouponRequest.getOutTradeOrderNo());
    }

    /**
     * 修改优惠券记录的状态
     * @param userId 当前用户的id
     * @param lockCouponRequest 需要锁定的优惠券信息
     */
    private void updateLockStatusForCouponRecord(Long userId, LockCouponRequest lockCouponRequest) {
        // 1.获取要锁定的优惠券记录id的集合
        List<Long> lockedCouponRecordIds = lockCouponRequest.getLockedCouponRecordIds();

        // 2.批量更新这些优惠券的use_state字段
        int rows = couponRecordMapper.updateBatchUserState(userId, CouponStateEnum.USED.name(),
                lockedCouponRecordIds);

        // 3.对结果进行判断
        if (rows != lockCouponRequest.getLockedCouponRecordIds().size()) {
            throw new BizException(BizCodeEnum.COUPON_RECORD_LOCK_FAIL);
        }
    }

    /**
     * 添加优惠券锁定的记录
     * @param lockCouponRequest 需要锁定的优惠券信息
     */
    private List<CouponTaskDO> saveLockTask(LockCouponRequest lockCouponRequest) {
        // 1.构建优惠券锁定记录的集合
        List<CouponTaskDO> list = lockCouponRequest.getLockedCouponRecordIds().stream().map(item -> {
            CouponTaskDO couponTaskDO = new CouponTaskDO();
            couponTaskDO.setCouponRecordId(item);
            couponTaskDO.setOutTradeNo(lockCouponRequest.getOutTradeOrderNo());
            couponTaskDO.setCreateTime(new Date());
            couponTaskDO.setLockState(LockStateEnum.LOCK.name());
            return couponTaskDO;
        }).collect(Collectors.toList());

        // 2.批量添加这些记录
        int rows = couponTaskMapper.saveBatchTask(list);

        // 3.对结果进行判断
        if (rows != lockCouponRequest.getLockedCouponRecordIds().size()) {
            throw new BizException(BizCodeEnum.COUPON_RECORD_LOCK_FAIL);
        }

        // 4.返回结果
        return list;
    }

    /**
     * 发送延时消息，这个消息是当订单没有成功支付时，用来解锁优惠券的
     * @param list 优惠券锁定记录
     * @param outTradeNo 订单号
     */
    private void sendDelayMessageForReleaseLock(List<CouponTaskDO> list, String outTradeNo) {
        for (CouponTaskDO couponTaskDO : list) {
            CouponRecordMessage couponRecordMessage = new CouponRecordMessage();
            couponRecordMessage.setOutTradeNo(outTradeNo);
            couponRecordMessage.setTaskId(couponTaskDO.getId());
            rabbitTemplate.convertAndSend(rabbitMQProperties.getCouponEventExchange(),
                    rabbitMQProperties.getCouponReleaseDelayRoutingKey(), couponRecordMessage);
            log.info("优惠券锁定消息发送成功：{}", couponRecordMessage);
        }
    }
}
